#include "Col.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Class.h"

#include "Error.h"
#include "Storage.h"
#include "String.h"
#include "ObjArray.h"
#include "OrdColl.h"
#include "ObjectTable.h"
#include "ClassManager.h"
#include "Env.h"
#include "AccessMem.h"
#include "ET_stdio.h"

static const double cClassVersion= 3.1;

#ifdef ET_
	static char *EtPrefix= _QUOTE_(ET_);
#else
	static char *EtPrefix= "";
#endif

extern void _InitModule(), _TermModule();

//---- Class -------------------------------------------------------------------

NewMetaImpl(Class,Object, (T(version), TP(super), TP(className), T(size),
		TP(proto), TS(declFileName), T(declFileLine), TS(implFileName),
		T(implFileLine), TP(subclasses), T(instanceCount),
		TP(methods)));

Class *Class::classptr;

Class::Class(const char *name, int sz, Object *pro, const char *implfn,
		const char *declfn, int il, int dl, bool abstract, double vers)
{
	Init(name, sz, pro, implfn, declfn, il, dl, abstract, vers);
	showfunc= 0;
}

Class::Class(const char *name, int sz, Class *sup, Object *pro, const char *implfn,
		const char *declfn, int il, int dl, bool abstract, double vers)
{
	Init(name, sz, pro, implfn, declfn, il, dl, abstract, vers);
	showfunc= 0;
	super= sup;
}

Class::Class(const char *name, int sz, const char *implfn, int il, classShowFunc f)
{
	Init(name, sz, 0, implfn, implfn, il, il, FALSE, cClassVersion);
	showfunc= f;
}

Class::Class(const char *name, double vers)
{
	className= name;
	version= vers;
	proto= 0;
	methods= 0;
	subclasses= 0;
	showfunc= 0;
}

void Class::Init(const char *name, int sz, Object *pro, const char *implfn,
			const char *declfn, int il, int dl, bool abstract, double vers)
{
	size= sz;
	if (vers == 0.0)
		version= cClassVersion;
	else
		version= vers;
	className= name;
	if (strncmp(name, EtPrefix, sizeof(EtPrefix)-1) == 0)
		className= &name[sizeof(EtPrefix)-1];
	if (gDebug)
		fprintf(stderr, "%s\n", className);
	declFileName= declfn;
	implFileName= implfn;
	declFileLine= dl;
	implFileLine= il;
	proto= pro;
	subclasses= 0;
	instanceCount= 0;
	methods= 0;
	SetFlag(eClassAbstract, abstract);
	if (gClassManager == 0)
		gClassManager= new ClassManager;
	gClassManager->Add(this);
	SetFlag(eClassObject, proto != 0);
	classptr= 0;
	_InitModule();
}

Class::~Class()
{
	if (proto) {
		delete (void*) proto;   // avoid call of destructor for prototype
		proto= 0;
	}
	if (methods) {
		methods->FreeAll();
		SafeDelete(methods);
	}
	SafeDelete(subclasses);
	if (gClassManager && gClassManager->Remove(this))
		_TermModule();
}

//---- class hierarchy ---------------------------------------------------------

void Class::SetSuper()
{
	super= classptr;
	classptr= this;
}

bool Class::isKindOf(register Class *c)
{
	register Class *cc;

	for (cc= this; cc; cc= cc->super)
		if (cc == c)
			return TRUE;
	return FALSE;
}

void Class::AddSubclass(Class *cl)
{
	if (subclasses == 0)
		subclasses= new OrdCollection;
	subclasses->Add(cl);
}

Iterator *Class::SubclassIterator()
{
	if (subclasses == 0)
		subclasses= new OrdCollection;
	return subclasses->MakeIterator();
}

//---- comparing ---------------------------------------------------------------

u_long Class::Hash()
{
	register u_long hash;
	register const char *p= className;

	for (hash= 0; *p; p++)
		hash= (hash << 1) ^ *p;
	return hash;
}

bool Class::IsEqual(Object *op)
{
	if (!op->IsKindOf(Class))
		return FALSE;
	return strcmp(className, ((Class*)op)->className) == 0;
}

int Class::Compare(Object *op)
{
	return strcmp(className, Guard(op,Class)->className);
}

//---- converting, input/output ------------------------------------------------

const char *Class::AsString()
{
	return (char*) className;
}

void Class::Show(char *buf, void *addr, int l, bool isptr)
{
	if (showfunc) {
		showfunc(buf, addr, l, isptr);
	} else {
		if (isptr) {
			if (ObjectTable::PtrIsValid((Object*)addr))
				sprintf(buf, "<%s>", ((Object*)addr)->ClassName());
		} else
			sprintf(buf, "<inline>");
	}
}

OStream &Class::PrintOn(OStream &os)
{
	return os << className SP << cClassVersion SP;
}

// always called on prototype object !!!
Object *Class::ReadAndMap(IStream &is)
{
	char name[100];
	double vers;

	is >> name >> vers;

	Class *clp= gClassManager->FindOrLoad(name, vers);
	if (clp) {
		if (vers < clp->Version())
			fprintf(stderr, "older version of: %s (%g)\n", name, vers);
		else if (vers > clp->Version())
			fprintf(stderr, "newer version of: %s (%g)\n", name, vers);
		clp->SetLoadVersion(vers);
	}
	return clp;
}

//---- members -----------------------------------------------------------------

Object *Class::SomeMember()
{
	return ObjectTable::SomeMember(this);
}

void Class::EnumerateMyMembers(AccessMembers *ac)
{
	if (className && ac)
		ac->ClassName((char*)className);
	if (proto)
		proto->Members(ac);
}

void Class::EnumerateMembers(AccessMembers *ac)
{
	EnumerateMyMembers(ac);
	if (super)
		super->EnumerateMembers(ac);
}

Object *Class::SomeInstance()
{
	return ObjectTable::SomeInstance(this);
}

